package com.struc.td;

import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;

//this is our ingame state class
public class Game extends State implements InputProcessor{
	private Array<Enemy> enemies;
	private Array<Enemy> enemiesToRemove;
	private Array<TowerSpace> towerSpots;
	private Array<TowerSpace> towerSpotsToRemove;
	private Array<Tower> towers;
	private Array<Tower> soldTowers;
	private Entity activeEntity;
	private SpriteBatch spriteBatch;
	private OrthographicCamera gameCam;
	private OrthographicCamera guiCam;
	private Texture bgImTxt;
	private Sprite backgroundImage;
	private Sprite pauseButton;
	private Sprite soundOnButton;
	private Sprite soundOffButton;
	private Sprite talentButton;
	private Sprite goldspr;
	private int currentLevel;
	private int enemiesOnLevel;
	private double timer;
	private boolean isTouched;
	private Sprite soundButton;
	boolean clickedGui;
	private int touchedX;
	private int touchedY;
	private Main main;
	private Player player;
	private BitmapFont font;
	private long clickedEntityAt;
	private Entity firstClick;
	private ButtonSelect buttonSelect;
	private int timeSinceLastCoin;
	private int timeToNextCoin;
	private Random random;
	//our constructor that creates a fresh game.
	public Game(Main m) {
		Sounds.mute();
		Gdx.graphics.setVSync(true);
		player = new Player();
		soundButton = new Sprite();
		enemies = new Array<Enemy>();
		enemiesToRemove = new Array<Enemy>();
		towerSpots = new Array<TowerSpace>();
		towerSpotsToRemove = new Array<TowerSpace>();
		towers = new Array<Tower>();
		soldTowers = new Array<Tower>();
		isTouched = false;
		clickedGui = false;
		buttonSelect = null;
		gameCam = new OrthographicCamera();
		guiCam = new OrthographicCamera();
		guiCam.position.set(Main.WIDTH/2,Main.HEIGHT/2,0);
		bgImTxt = StaticTextures.game_worldmap;
		backgroundImage = new Sprite(bgImTxt);
		backgroundImage.setPosition(0,0);
		spriteBatch = new SpriteBatch();
		currentLevel = 0;
		main = m;
		addTowerSpots();
		gameCam.zoom = 2f;
		gameCam.position.set(backgroundImage.getWidth()/2,Gdx.graphics.getHeight(),0);
		goldspr = new Sprite(StaticTextures.gold_text, 0, 0, 128, 32);
		player.setGold(800);
		font = new BitmapFont(Gdx.files.internal("data/towerbone2.fnt"), Gdx.files.internal("data/towerbone2.png"), false);
		pauseButton = new Sprite(StaticTextures.pausebutton);
		talentButton = new Sprite(StaticTextures.talentbutton);
		soundOnButton = new Sprite(StaticTextures.soundon);
		soundOffButton = new Sprite(StaticTextures.soundoff);
		main.setGameOn(false);
		soundOnButton.setBounds(0,0,30,30);
		soundOffButton.setBounds(0,0,30,30);
		timeSinceLastCoin = 0;
		timeToNextCoin = 0;
		random = new Random();
	}

	//method that will reset a game to a new game state
	public void restart() {
		player = new Player();
		enemies.clear();
		enemiesToRemove.clear();
		towerSpots.clear();
		towers.clear();
		soldTowers.clear();
		isTouched = false;
		clickedGui = false;
		currentLevel = 0;
		buttonSelect = null;
		addTowerSpots();
		player.setGold(450);
		activeEntity = null;
	}

	//adding towerspots
	public void addTowerSpots(){
		towerSpots.add(new TowerSpace(180,0));
		towerSpots.add(new TowerSpace(180,80));
		towerSpots.add(new TowerSpace(260,120));
		towerSpots.add(new TowerSpace(340,170));
		towerSpots.add(new TowerSpace(340,250));
		towerSpots.add(new TowerSpace(650,370));
		towerSpots.add(new TowerSpace(640,440));
		towerSpots.add(new TowerSpace(560,420));
		towerSpots.add(new TowerSpace(570,350));
		towerSpots.add(new TowerSpace(480,400));
		towerSpots.add(new TowerSpace(480,320));
		towerSpots.add(new TowerSpace(400,340));
		towerSpots.add(new TowerSpace(420,240));
		towerSpots.add(new TowerSpace(400,0));
		towerSpots.add(new TowerSpace(480,30));
		towerSpots.add(new TowerSpace(560,70));
		towerSpots.add(new TowerSpace(620,160));
		towerSpots.add(new TowerSpace(640,80));
		towerSpots.add(new TowerSpace(700,190));
		towerSpots.add(new TowerSpace(780,210));
		towerSpots.add(new TowerSpace(850,340));
		towerSpots.add(new TowerSpace(830,420));
		towerSpots.add(new TowerSpace(810,500));
		towerSpots.add(new TowerSpace(790,580));
		towerSpots.add(new TowerSpace(710,620));
		towerSpots.add(new TowerSpace(630,610));
		towerSpots.add(new TowerSpace(550,600));
		towerSpots.add(new TowerSpace(470,590));
		towerSpots.add(new TowerSpace(390,575));
		towerSpots.add(new TowerSpace(310,560));
		towerSpots.add(new TowerSpace(240,500));
		towerSpots.add(new TowerSpace(215,420));
		towerSpots.add(new TowerSpace(180,160));
		towerSpots.add(new TowerSpace(100,230));
		towerSpots.add(new TowerSpace(120,650));
		towerSpots.add(new TowerSpace(250,720));
		towerSpots.add(new TowerSpace(820,760));
		towerSpots.add(new TowerSpace(820,680));
	}

	//will resize camera settings to suit preferred settings
	public void resized() {
		guiCam.position.set(Main.WIDTH/2,Main.HEIGHT/2,0);
		gameCam.viewportHeight = Main.HEIGHT;
		gameCam.viewportWidth = Main.WIDTH;
		guiCam.viewportHeight = Main.HEIGHT;
		guiCam.viewportWidth = Main.WIDTH;
		GL10 gl = Gdx.graphics.getGL10();
		gl.glViewport(0,0,Main.WIDTH,Main.HEIGHT);
		gameCam.update();
		gameCam.apply(gl);
		guiCam.update();
		guiCam.apply(gl);
	}

	/* This method will iterate through all entities in the game
	 * and call their respective update methods. It also removes
	 * any dead enemy and sold tower.
	 */
	public void update() {
		if (TalentTree.talents.get(0).isUnlocked()) {
			timeSinceLastCoin += Gdx.graphics.getDeltaTime()*1000;
			if (timeToNextCoin == 0) {
				timeToNextCoin = random.nextInt(3000);
				timeToNextCoin += timeSinceLastCoin+4000;
			}
			if (timeSinceLastCoin > timeToNextCoin) {
				System.out.println("COIN!");
				timeSinceLastCoin = 0;
				timeToNextCoin = random.nextInt(3000);
				timeToNextCoin += timeSinceLastCoin+4000;
			}
		}
		for (Enemy e : enemies) {
			e.update();
			if (e.getDead()) {
				enemiesToRemove.add(e);
				player.incGold(e.getValue());
			}
			if (e.reachedGoal()) {
				main.setGameOn(false);
				main.setState(5);
			}
		}
		for (Enemy e : enemiesToRemove) {
			enemies.removeValue(e,true);
		}
		enemiesToRemove.clear();

		for (Tower t : towers) {
			if(t.getSold()){
				soldTowers.add(t);
			}
			t.update();
		}
		for(Tower t : soldTowers){
			towers.removeValue(t,true);
		}
		soldTowers.clear();

		for (TowerSpace ts : towerSpotsToRemove) {
			towerSpots.removeValue(ts, true);
		}
		towerSpotsToRemove.clear();


		if (enemies.size == 0) {
			timer += Gdx.graphics.getDeltaTime()*1000;
			if(timer < 500){
				timer -= 500;
				currentLevel++;
				Level level = new Level(currentLevel);
				enemies = level.getLevel();
				enemiesOnLevel = enemies.size;
			}
		}
		if(Sounds.getMuted()){
			soundButton = soundOffButton;
		}
		else{
			soundButton = soundOnButton;
		}

	}

	//returns an array of enemys
	public Array<Enemy> getEnemyList(){
		return enemies;
	}

	/* Renders background, iterates through
	 * all entities in game and renders them
	 * in a suitable order.
	 */
	public void render() {
		spriteBatch.setProjectionMatrix(gameCam.combined);
		spriteBatch.begin();
		spriteBatch.disableBlending();
		backgroundImage.draw(spriteBatch);
		spriteBatch.enableBlending();

		for (TowerSpace ts : towerSpots) {
			if (ts == activeEntity) {
				ts.renderSelected(spriteBatch);
			} else {
				ts.render(spriteBatch);
			}
		}

		for (Tower t : towers) {
			t.render(spriteBatch);
		}

		for (Enemy e : enemies) {
			e.render(spriteBatch);
		}
		spriteBatch.disableBlending();
		for (Enemy e : enemies) {
			e.renderHp(spriteBatch);
		}
		for (Tower t : towers){
			t.renderCd(spriteBatch);
		}

		spriteBatch.enableBlending();
		if (activeEntity instanceof Tower) {
			((Tower) activeEntity).renderRange(spriteBatch);
		}
		spriteBatch.end();
		spriteBatch.setProjectionMatrix(guiCam.combined);
		spriteBatch.begin();
		goldspr.setBounds(pauseButton.getWidth() + 10, Gdx.graphics.getHeight()-30, 128/1.2f, 32/1.2f);
		goldspr.draw(spriteBatch);
		font.draw(spriteBatch," " + player.getGold(),95f/256*Gdx.graphics.getWidth(), 475/512f*Gdx.graphics.getHeight());
		if (activeEntity != null) {
			activeEntity.renderInfo(spriteBatch);
			if (activeEntity instanceof Enemy) {
				if (((Enemy) activeEntity).getDead()) {
					activeEntity = null;
				}
			} else if (activeEntity instanceof TowerSpace) {
				if (!towerSpots.contains((TowerSpace) activeEntity,true)) {
					activeEntity = null;
				}
			}
			else if (activeEntity instanceof Tower){
				((Tower) activeEntity).renderButtonSelect(spriteBatch, buttonSelect);
			}
		}
		pauseButton.setBounds(0,Gdx.graphics.getHeight()-30,30,30);
		talentButton.setBounds(0,Gdx.graphics.getHeight()-60,30,30);
		talentButton.draw(spriteBatch);
		pauseButton.draw(spriteBatch);
		soundButton.draw(spriteBatch);
		spriteBatch.end();
	}

	public void enter() {
		Gdx.input.setInputProcessor(this);
	}

	public boolean keyDown(int arg0) {
		return false;
	}

	public boolean keyTyped(char arg0) {
		return false;
	}

	public boolean keyUp(int arg0) {
		return false;
	}

	public boolean scrolled(int arg0) {
		return false;
	}
	/* Called when the user touches the screen.
	 * Takes care of all input handling.
	 */
	public boolean touchDown(int x, int y, int arg2, int arg3) {
		Vector3 vecGame = new Vector3();
		vecGame.x = x;
		vecGame.y = y;
		gameCam.unproject(vecGame);
		touchedX = x;
		touchedY = y;
		if (activeEntity == null || !activeEntity.getInfoBounds().contains(x,y)) {
			isTouched = true;

			for (TowerSpace sp : towerSpots) {
				if (sp.getBounds().contains(vecGame.x,vecGame.y)) {
					firstClick = sp;
					clickedEntityAt = System.currentTimeMillis();
					break;
				}
			}


			for (Tower t : towers) {
				if (t.getBounds().contains(vecGame.x,vecGame.y)) {
					firstClick = t;
					clickedEntityAt = System.currentTimeMillis();
					break;
				}
			}

			for (Enemy e : enemies) {
				if (e.getBounds().contains(vecGame.x,vecGame.y)) {
					firstClick = e;
					clickedEntityAt = System.currentTimeMillis();
					break;
				}
			}
			y = Gdx.graphics.getHeight()-y;
			if (pauseButton.getBoundingRectangle().contains(x,y)) {
				main.setState(4);
			}
			if (talentButton.getBoundingRectangle().contains(x,y)) {
				main.setState(6);
			}
			if (soundButton.getBoundingRectangle().contains(x,y)) {
				Sounds.mute();
			}
		} else {
			y = Gdx.graphics.getHeight()-y;
			if (activeEntity instanceof Tower) {
				if (((Tower) activeEntity).getExitInfoRect().contains(x,y)) {
					activeEntity = null;
				} else if (((Tower) activeEntity).getUpgradeSpecialRect().contains(x,y)) {
					((Tower) activeEntity).upgradeSpecial(player);
					buttonSelect = ButtonSelect.Special;
				} else if (((Tower) activeEntity).getUpgradeRangeRect().contains(x,y)) {
					((Tower) activeEntity).upgradeRange(player);
					buttonSelect = ButtonSelect.Range;
				} else if (((Tower) activeEntity).getSoldRect().contains(x,y)){
					((Tower) activeEntity).sell(player);
					buttonSelect = ButtonSelect.Sell;
					towerSpots.add(new TowerSpace((int)((Tower) activeEntity).getBounds().getX(),(int)((Tower) activeEntity).getBounds().getY()));
					activeEntity = null;
				}
			} else if (activeEntity instanceof Enemy) {
				if (((Enemy) activeEntity).getExitInfoRect().contains(x,y)) {
					activeEntity = null;
				}
			} else if (activeEntity instanceof TowerSpace) {
				if (((TowerSpace) activeEntity).getExitInfoRect().contains(x,y)) {
					activeEntity = null;
				} else if (((TowerSpace) activeEntity).getDamageTowerRect().contains(x,y)) {
					if (player.getGold() > 100) {
						towers.add(new TowerDamage((int)((TowerSpace) activeEntity).getBounds().getX(),(int)((TowerSpace) activeEntity).getBounds().getY(),this));
						towerSpotsToRemove.add((TowerSpace) activeEntity);
						player.incGold(-100);
						activeEntity = towers.get(towers.size-1);
					}
				} else if (((TowerSpace) activeEntity).getSlowTowerRect().contains(x,y)) {
					if (player.getGold() > 100) {
						towers.add(new TowerSlow((int)((TowerSpace) activeEntity).getBounds().getX(),(int)((TowerSpace) activeEntity).getBounds().getY(),this));
						towerSpotsToRemove.add((TowerSpace) activeEntity);
						player.incGold(-100);
						activeEntity = towers.get(towers.size-1);
					}
				}
			}
		}
		return false;
	}

	/* Called when the user drags his/her finger
	 * on the screen. Used to change the position
	 * of the camera.
	 */
	public boolean touchDragged(int x, int y, int arg2) {
		if (isTouched) {
			int deltaX = x-touchedX;
			int deltaY = y-touchedY;
			gameCam.position.x -= deltaX*2;
			gameCam.position.y += deltaY*2;
			if (gameCam.position.x < Gdx.graphics.getWidth()) {
				gameCam.position.x = Gdx.graphics.getWidth();
			} else if (gameCam.position.x > backgroundImage.getWidth()-Gdx.graphics.getWidth()) {
				gameCam.position.x = backgroundImage.getWidth()-Gdx.graphics.getWidth();
			}
			if (gameCam.position.y < Gdx.graphics.getHeight()) {
				gameCam.position.y = Gdx.graphics.getHeight();
			} else if (gameCam.position.y > backgroundImage.getHeight()-Gdx.graphics.getHeight()) {
				gameCam.position.y = backgroundImage.getHeight()-Gdx.graphics.getHeight();
			}
			touchedX = x;
			touchedY = y;
			gameCam.update();
		}
		return false;
	}

	public boolean touchMoved(int arg0, int arg1) {
		return false;
	}

	/* Called when the user removes his/her 
	 * finger from the screen. Used to only
	 * set active entity if the user tapped
	 * an entity and not if he/she is dragging
	 * the map.
	 */
	public boolean touchUp(int x, int y, int arg2, int arg3) {
		isTouched = false;
		buttonSelect = null;
		Vector3 vecGame = new Vector3();
		vecGame.x = x;
		vecGame.y = y;
		gameCam.unproject(vecGame);
		touchedX = x;
		touchedY = y;
		if (activeEntity == null || !activeEntity.getInfoBounds().contains(x,y)) {
			isTouched = true;
			for (TowerSpace sp : towerSpots) {
				if (sp.getBounds().contains(vecGame.x,vecGame.y)) {
					if (System.currentTimeMillis()-clickedEntityAt < 200 && sp == firstClick) {
						activeEntity = sp;
						firstClick = null;
					}
					break;
				}
			}
			for (Tower t : towers) {
				if (t.getBounds().contains(vecGame.x,vecGame.y)) {
					if (System.currentTimeMillis()-clickedEntityAt < 200 && t == firstClick) {
						activeEntity = t;
						firstClick = null;
					}
					break;
				}
			}
			for (Enemy e : enemies) {
				if (e.getBounds().contains(vecGame.x,vecGame.y)) {
					if (System.currentTimeMillis()-clickedEntityAt < 200 && e == firstClick) {
						activeEntity = e;
						firstClick = null;
					}
					break;
				}
			}
		}
		return false;
	}
	//returns currentlevel
	public int getLevel() {
		return currentLevel;
	}
	//returns how many enemies we have killed on the level
	public int getLevelProgress() {
		return enemiesOnLevel-enemies.size;
	}
	// Methods only used for testing:
	public Entity getActiveEntity() {
		return activeEntity;
	}
	//returns the player
	public Player getPlayer() {
		return player;
	}
	//returns towers
	public Array<Tower> getTowers() {
		return towers;
	}
}